<template>
    <div class="chat-box">
        <t-chat ref="chatRef" :clear-history="chatList.length > 0 && !isStreamLoad" :data="chatList"
            :text-loading="loading" :is-stream-load="isStreamLoad" style="height: 600px" @scroll="handleChatScroll"
            @clear="clearConfirm">
            <!-- 内容插槽 -->
            <template #content="{ item, index }">
                <t-chat-reasoning v-if="item.reasoning?.length > 0" expand-icon-placement="right">
                    <template #header>
                        <t-chat-loading v-if="isStreamLoad && item.content.length === 0" text="思考中..." />
                        <div v-else style="display: flex; align-items: center">
                            <CheckCircleIcon
                                style="color: var(--td-success-color-5); font-size: 20px; margin-right: 8px" />
                            <span>已深度思考</span>
                        </div>
                    </template>
                    <t-chat-content v-if="item.reasoning.length > 0" :content="item.reasoning" />
                </t-chat-reasoning>
                <t-chat-content v-if="item.content.length > 0" :content="item.content" />
            </template>

            <!-- 操作按钮插槽 -->
            <template #actions="{ item, index }">
                <t-chat-action :content="item.content" :operation-btn="['good', 'bad', 'replay', 'copy']"
                    @operation="handleOperation" />
            </template>

            <!-- 底部输入框 -->
            <template #footer>
                <t-chat-input :stop-disabled="isStreamLoad" @send="inputEnter" @stop="onStop" />
            </template>
        </t-chat>

        <!-- 回到底部按钮 -->
        <t-button v-show="isShowToBottom" variant="text" class="bottomBtn" @click="backBottom">
            <div class="to-bottom">
                <ArrowDownIcon />
            </div>
        </t-button>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { MockSSEResponse } from './mock-data/sseRequest-reasoning';
import { ArrowDownIcon, CheckCircleIcon } from 'tdesign-icons-vue-next';

// 定义消息项类型
interface ChatItem {
    avatar?: string;
    name?: string;
    datetime?: string;
    content: string;
    reasoning: string;
    role: 'user' | 'assistant' | 'error' | 'model-change';
    duration?: number;
}

// 定义 fetch 控制器类型
interface FetchCancel {
    controller: {
        close: () => void;
    };
}

// 响应式数据
const loading = ref<boolean>(false);
const isStreamLoad = ref<boolean>(false);
const chatRef = ref<HTMLElement | null>(null); // 实际应为 TChat 实例类型，若组件有导出可替换
const isShowToBottom = ref<boolean>(false);
const chatList = ref<ChatItem[]>([
    {
        content: `模型由<span>hunyuan</span>变为<span>GPT4</span>`,
        role: 'model-change',
        reasoning: '',
    },
    {
        avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png',
        name: 'TDesignAI',
        datetime: '今天16:38',
        reasoning: '',
        content: '它叫 McMurdo Station ATM，是美国富国银行安装在南极洲最大科学中心麦克默多站的一台自动提款机。',
        role: 'assistant',
        duration: 10,
    },
    {
        avatar: 'https://tdesign.gtimg.com/site/avatar.jpg',
        name: '自己',
        datetime: '今天16:38',
        content: '南极的自动提款机叫什么名字？',
        role: 'user',
        reasoning: '',
    },
]);

// 取消请求的引用
const fetchCancel = ref<FetchCancel | null>(null);

// 滚动到底部
const backBottom = (): void => {
    if (chatRef.value && typeof (chatRef.value as any).scrollToBottom === 'function') {
        (chatRef.value as any).scrollToBottom({
            behavior: 'smooth',
        });
    }
};

// 处理滚动事件
const handleChatScroll = ({ e }: { e: Event }): void => {
    const target = e.target as HTMLElement;
    isShowToBottom.value = target.scrollTop < 0;
};

// 清空聊天记录
const clearConfirm = (): void => {
    chatList.value = [];
};

// 操作按钮回调
const handleOperation = (type: string, options: any): void => {
    console.log('handleOperation', type, options);
};

// 停止流式请求
const onStop = (): void => {
    if (fetchCancel.value) {
        fetchCancel.value.controller.close();
        loading.value = false;
        isStreamLoad.value = false;
    }
};

// 输入框发送消息
const inputEnter = (inputValue: string): void => {
    if (isStreamLoad.value || !inputValue) return;

    const params: ChatItem = {
        avatar: 'https://tdesign.gtimg.com/site/avatar.jpg',
        name: '自己',
        datetime: new Date().toDateString(),
        content: inputValue,
        role: 'user',
        reasoning: '',
    };
    chatList.value.unshift(params);

    const params2: ChatItem = {
        avatar: 'https://tdesign.gtimg.com/site/chat-avatar.png',
        name: 'TDesignAI',
        datetime: new Date().toDateString(),
        content: '',
        reasoning: '',
        role: 'assistant',
    };
    chatList.value.unshift(params2);

    handleData(inputValue);
};

// 模拟 SSE 请求函数
const fetchSSE = async (
    fetchFn: () => Promise<Response>,
    options: {
        success: (data: any) => void;
        fail?: () => void;
        complete: (isOk: boolean, msg?: string) => void;
    }
): Promise<void> => {
    let reader: ReadableStreamDefaultReader | undefined;
    try {
        const response = await fetchFn();
        if (!response.ok) {
            options.complete(false, response.statusText);
            options.fail?.();
            return;
        }

        reader = response.body?.getReader();
        const decoder = new TextDecoder();

        if (!reader) return;

        const readChunk = async (): Promise<void> => {
            const { done, value } = await reader!.read();
            if (done) {
                options.complete(true);
                return;
            }

            const chunk = decoder.decode(value, { stream: true });
            const lines = chunk.split(/\r?\n/).filter(line => line.trim() !== '');
            for (const line of lines) {
                try {
                    const jsonData = JSON.parse(line);
                    options.success(jsonData);
                } catch (e) {
                    console.warn('Parse SSE data failed:', e);
                }
            }

            await readChunk();
        };

        await readChunk();
    } catch (error) {
        options.complete(false, (error as Error).message);
        options.fail?.();
    } finally {
        reader?.cancel();
    }
};

// 处理数据流
const handleData = async (inputValue: string): Promise<void> => {
    loading.value = true;
    isStreamLoad.value = true;

    const lastItem = chatList.value[0];
    const mockedData = {
        reasoning: `嗯，用户问牛顿第一定律是不是适用于所有参考系。首先，我得先回忆一下牛顿第一定律的内容。牛顿第一定律，也就是惯性定律，说物体在没有外力作用时会保持静止或匀速直线运动。也就是说，保持原来的运动状态。
  
  那问题来了，这个定律是否适用于所有参考系呢？记得以前学过的参考系分惯性系和非惯性系。惯性系里，牛顿定律成立；非惯性系里，可能需要引入惯性力之类的修正。所以牛顿第一定律应该只在惯性参考系中成立，而在非惯性系中不适用，比如加速的电梯或者旋转的参考系，这时候物体会有看似无外力下的加速度，所以必须引入假想的力来解释。`,
        content: `牛顿第一定律（惯性定律）**并不适用于所有参考系**，它只在**惯性参考系**中成立。以下是关键点：
  
  ---
  
  ### **1. 牛顿第一定律的核心**
  - **内容**：物体在不受外力（或合力为零）时，将保持静止或匀速直线运动状态。
  - **本质**：定义了惯性系的存在——即存在一类参考系，在其中惯性定律成立。`,
    };

    const mockResponse = new MockSSEResponse([{ data: mockedData }]);
    fetchCancel.value = mockResponse as unknown as FetchCancel;

    await fetchSSE(
        () => mockResponse.getResponse(),
        {
            success(result) {
                console.log('success', result);
                loading.value = false;
                if (result.delta?.reasoning_content) {
                    lastItem.reasoning += result.delta.reasoning_content;
                }
                if (result.delta?.content) {
                    lastItem.content += result.delta.content;
                }
            },
            complete(isOk, msg) {
                if (!isOk) {
                    lastItem.role = 'error';
                    lastItem.content = msg || '请求失败';
                    lastItem.reasoning = msg || '请求失败';
                }
                lastItem.duration = 20;
                isStreamLoad.value = false;
                loading.value = false;
            },
        }
    );
};
</script>

<style lang="less" scoped>
/* 滚动条样式 */
::-webkit-scrollbar-thumb {
    background-color: var(--td-scrollbar-color);
}

::-webkit-scrollbar-thumb:horizontal:hover {
    background-color: var(--td-scrollbar-hover-color);
}

::-webkit-scrollbar-track {
    background-color: var(--td-scroll-track-color);
}

.chat-box {
    position: relative;

    .bottomBtn {
        position: absolute;
        left: 50%;
        margin-left: -20px;
        bottom: 210px;
        padding: 0;
        border: 0;
        width: 40px;
        height: 40px;
        border-radius: 50%;
        box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08),
            0px 16px 24px 2px rgba(0, 0, 0, 0.04),
            0px 6px 30px 5px rgba(0, 0, 0, 0.05);
    }

    .to-bottom {
        width: 40px;
        height: 40px;
        border: 1px solid #dcdcdc;
        box-sizing: border-box;
        background: var(--td-bg-color-container);
        border-radius: 50%;
        font-size: 24px;
        line-height: 40px;
        display: flex;
        align-items: center;
        justify-content: center;

        .t-icon {
            font-size: 24px;
        }
    }
}

.model-select {
    display: flex;
    align-items: center;

    .t-select {
        width: 112px;
        height: 32px;
        margin-right: 8px;

        .t-input {
            border-radius: 32px;
            padding: 0 15px;
        }
    }

    .check-box {
        width: 112px;
        height: 32px;
        border-radius: 32px;
        border: 0;
        background: #e7e7e7;
        color: rgba(0, 0, 0, 0.9);
        box-sizing: border-box;
        flex: 0 0 auto;

        .t-button__text {
            display: flex;
            align-items: center;
            justify-content: center;

            span {
                margin-left: 4px;
            }
        }
    }

    .check-box.is-active {
        border: 1px solid #d9e1ff;
        background: #f2f3ff;
        color: var(--td-brand-color);
    }
}
</style>